package mskruch.gps.data;


import generated.tcx.ActivityLapT;
import generated.tcx.ActivityT;
import generated.tcx.TrackT;
import generated.tcx.TrackpointT;
import generated.tcx.TrainingCenterDatabaseT;
import gpstools.data.UnsupportedFormatException;

import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;

import mskruch.gps.Lap;
import mskruch.gps.Point;
import mskruch.gps.PointFactory;
import mskruch.gps.Track;

/**
 * TCX Content Loader
 * 
 * @author Marcin Skruch
 */
@Deprecated
public class TcxLoader implements TrackLoaderAPI {
    private List<Track> tracks = new ArrayList<Track>();
    private int index = 0;

    public TcxLoader(byte[] data) throws UnsupportedFormatException {
        this(new ByteArrayInputStream(data));
    }

    public TcxLoader(String fileName) throws FileNotFoundException, UnsupportedFormatException {
        this(new FileInputStream(fileName));
    }

    public TcxLoader(InputStream inputStream) throws UnsupportedFormatException {
        try {
            final JAXBElement<TrainingCenterDatabaseT> root = unmarshal(inputStream);
            final TrainingCenterDatabaseT database = root.getValue();
            
            if (database == null){
                throw new UnsupportedFormatException("no data");
            }

            List<ActivityT> activities = database.getActivities().getActivity();
            for (ActivityT activity : activities) {
                List<Lap> laps = new ArrayList<Lap>();

                List<ActivityLapT> tcxLaps = activity.getLap();
                for (ActivityLapT tcxLap : tcxLaps) {
                    List<Point> points = new ArrayList<Point>();

                    List<TrackT> tcxTracks = tcxLap.getTrack();
                    for (TrackT tcxTrack : tcxTracks) {
                        List<TrackpointT> tcxPoints = tcxTrack.getTrackpoint();
                        if (!tcxPoints.isEmpty()) {
                            for (TrackpointT tcxPoint : tcxPoints) {
                                Point point = processPoint(tcxPoint);
                                if (point != null) {
                                    points.add(point);
                                }
                            }
                        }
                    }
                    if (!points.isEmpty()) {
                        laps.add(new Lap(points.toArray(new Point[points.size()])));
                    }
                }

                if (!laps.isEmpty()) {
                    tracks.add(new Track(activity.getNotes(), laps.toArray(new Lap[laps.size()])));
                }

            }
        } catch (JAXBException e) {
            throw new UnsupportedFormatException(e);
        }
    }

    public Track[] getTracks() {
        return tracks.toArray(new Track[tracks.size()]);
    }

    public Track nextTrack() {
        if (index < tracks.size()) {
            return tracks.get(index++);
        } else {
            return null;
        }
    }

    private static JAXBElement<TrainingCenterDatabaseT> unmarshal(InputStream input) throws JAXBException {
        JAXBContext context = JAXBContext.newInstance(TrainingCenterDatabaseT.class);
        Unmarshaller unmarshaller = context.createUnmarshaller();
        @SuppressWarnings("unchecked")
        JAXBElement<TrainingCenterDatabaseT> root = (JAXBElement<TrainingCenterDatabaseT>) unmarshaller
                .unmarshal(input);
        return root;
    }

    private static Point processPoint(TrackpointT tcxPoint) {
        /* standard */
        try {
            final double lon = tcxPoint.getPosition().getLongitudeDegrees();
            final double lat = tcxPoint.getPosition().getLatitudeDegrees();
            final double ele = tcxPoint.getAltitudeMeters().doubleValue();
            final long time = tcxPoint.getTime().toGregorianCalendar().getTimeInMillis();

            /* extensions */
            Short hr = null;
            if (tcxPoint.getHeartRateBpm() != null) {
                hr = tcxPoint.getHeartRateBpm().getValue();
            }
            Short cad = null;
            if (tcxPoint.getCadence() != null) {
                cad = tcxPoint.getCadence();
            }

            return PointFactory.newPoint(lon, lat, ele, time, hr, cad);
        } catch (Exception e) {
            return null;
        }
    }

}
